package edu.ucla.cs.rpc.multicast;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;

import edu.ucla.cs.rpc.multicast.network.MulticastManager;
import edu.ucla.cs.rpc.multicast.network.message.RPCMessage;
import edu.ucla.cs.rpc.multicast.sequencer.Sequencer;
import edu.ucla.cs.rpc.multicast.sequencer.SequencerDest;
import edu.ucla.cs.rpc.multicast.sequencer.SequencerSender;
import edu.ucla.cs.rpc.multicast.sequencer.fixed.FixedSequencer;

/**
 * RPCClient dispatches a Java method call to a set of RPCServers. Currently,
 * the result of the method call is discarded at the remote side, so no result
 * is returned to the client.
 * 
 * @author Chase Covello Philip Russell
 * 
 */
public class RPCClient {

	private static class RPCTest implements Serializable {
		private static final long serialVersionUID = 1L;

		public void rpcTest(String message) {
			System.out.println(message);
		}
	}

	public static void main(String[] args) throws IOException,
			InterruptedException {
		// Start a MulticastManager
		MulticastManager manager = new MulticastManager();

		// We are using the fixed sequencer implementation for multicast
		Sequencer seq = new FixedSequencer(manager.getSocketAddress());

		// Initialize the RPCClient with the appropriate message sender
		// implementation
		RPCClient client = new RPCClient(new SequencerSender(seq
				.getSocketAddress()));

		// Create a set of RPCServers to receive RPC calls
		Set<RPCServer> servers = new HashSet<RPCServer>();
		for (int i = 0; i < 5; i++) {
			Thread.sleep(200);
			servers.add(new RPCServer(new SequencerDest(manager
					.getSocketAddress())));
		}
		Thread.sleep(1000);

		// RPCTest.rpcTest("Hello, world!");
		client.call(new RPCTest(), "rpcTest", "Hello, world!");

		// Shut down servers, sequencer, and multicast manager
		for (RPCServer server : servers)
			server.shutdown();
		seq.shutdown();
		manager.shutdown();
	}

	// Sender implementation: fixed, moving, privilege, comm. history, or dest.
	// agreement
	private final Sender sender;

	/**
	 * Constructs a new RPCClient with the given multicast Sender
	 * implementation.
	 * 
	 * @param sender
	 *            the Sender to use.
	 * @throws IOException
	 *             in the case of unrecoverable network errors.
	 */
	public RPCClient(Sender sender) throws IOException {
		this.sender = sender;
	}

	/**
	 * Dispatch a method to the set of remote RPCServers.
	 * 
	 * @param object
	 *            the object whose method is being called, or a Class in the
	 *            case of static methods.
	 * @param methodName
	 *            the method name.
	 * @param args
	 *            arguments to the method.
	 * @throws IOException
	 *             in the case of unrecoverable network errors.
	 */
	public void call(Serializable object, String methodName, Serializable... args)
			throws IOException {
		RPCMessage request = new RPCMessage(object, methodName, args,
				new InetSocketAddress(0));

		sender.totalOrderBroadcast(request);
	}
}
